package publish

import (
	"encoding/base64"
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntityManager"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnv"
	"gitee.com/fierce_wolf/go-fox-edge-common/commLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisExchange"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Number"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeEnv"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeTSQL"
	"gitee.com/fierce_wolf/go-fox-edge-iot-common/iotRemote"
	"github.com/go-redis/redis"
	"go-fox-edge-iot-thingspanel/component"
	"time"
)

type telemetryRTPublish struct {
}

func (e *telemetryRTPublish) publish() {
	// 定义一个函数级的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			commLogger.Console.Error(fmt.Sprintf("config() 捕获到异常:%v", r))
		}
	}()

	// 检查：是否到了执行的时间间隔
	ok, err := component.TimeIntervalService.TestLastTime(String.CamelName("telemetryRT"))
	if err != nil || !ok {
		return
	}

	entityType := edgeEntity.Type.DeviceSequenceEntity.Type()
	entity, err := commEntity.NewEntity(entityType)
	if err != nil {
		return
	}

	// 分批处理
	pageSize := 10
	for true {
		count, err := e.syncEntity(entityType, entity.TableName(), pageSize)
		if err != nil {
			break
		}

		if count != pageSize {
			break
		}
	}

}

func (e *telemetryRTPublish) syncEntity(entityType, tableName string, pageSize int) (int, error) {
	if iotRemote.Mqtt.Client == nil || !iotRemote.Mqtt.Client.IsConnected() {
		return 0, errors.New("mqtt尚未建立连接")
	}

	// 从redis读取上次上传的状态
	uploadCursor, _, err := commRedisExchange.ReadConfigValue(commEnv.Service.ServiceType, commEnv.Service.ServiceName, "uploadTelemetryRTCursor")
	if err != nil && err != redis.Nil {
		return 0, err
	}
	createTime := Map.GetInt64(uploadCursor, "createTime", 0)

	// 查询数据库的最新ID
	body := make(map[string]interface{})
	body["recordTime"] = createTime
	body["order"] = "ASC"
	body["pageSize"] = 10
	entityList, err := edgeTSQL.QueryRecords(body)
	if err != nil {
		return 0, err
	}
	if len(entityList) == 0 {
		return 0, nil
	}

	// 根据Fox-Edge下面的网关ID进行分组，网关ID的信息在通道。
	// 说明：下面可能是有网关的L3结构，此时gatewayEUI不为空，也可能是设备直连的L2结构，此时gatewayEUI为空
	gatewayGroup := e.groupByGatewayEUI(entityList)

	for gatewayEUI, gatewayList := range gatewayGroup {
		// 检查：如果gatewayEUI存在，也就是是LORA网关上传的数据，那么转换为thingsPanel的网关设备ID
		gatewayID := ""
		if gatewayEUI != "" {
			val, ok := component.LocalConfig.GatewayEUI.Load(gatewayEUI)
			if !ok || val == nil {
				continue
			}

			gatewayID = val.(string)
		}

		// 根据设备进行分组
		deviceGroup := e.groupByDeviceName(gatewayList)

		// 转为vo的格式
		vo := e.buildVO(gatewayID, gatewayEUI, deviceGroup)

		// thingsPanel要求先转换为jsn，再转为base64
		values := Json.BuildJson(vo)
		values = base64.StdEncoding.EncodeToString([]byte(values))

		body := make(map[string]interface{})
		body["device_id"] = component.LocalConfig.DeviceNumber
		body["values"] = values

		// 检测：mqtt是否连接
		if iotRemote.Mqtt.Client == nil || !iotRemote.Mqtt.Client.IsConnected() {
			return 0, errors.New("mqtt尚未建立连接")
		}

		// 发送mqtt数据
		if !iotRemote.Mqtt.Client.Publish("gateway/telemetry", Json.BuildJson(body), 10*1000) {
			return 0, errors.New("mqtt发送失败")
		}
	}

	// 保存游标状态到redis
	e.SaveCursorId(entityList, createTime)

	return len(entityList), nil
}

func (e *telemetryRTPublish) groupByDeviceName(entityList []*edgeEntity.DeviceSequenceEntity) map[string][]*edgeEntity.DeviceSequenceEntity {
	result := make(map[string][]*edgeEntity.DeviceSequenceEntity)

	for _, recordEntity := range entityList {
		list := result[recordEntity.DeviceName]
		if list == nil {
			list = make([]*edgeEntity.DeviceSequenceEntity, 0)
		}
		list = append(list, recordEntity)

		result[recordEntity.DeviceName] = list
	}

	return result
}

func (e *telemetryRTPublish) SaveCursorId(entityList []*edgeEntity.DeviceSequenceEntity, createTime int64) {
	maxCreateTime := int64(0)
	for _, entity := range entityList {
		if maxCreateTime < entity.GetBaseEntity().CreateTime {
			maxCreateTime = entity.GetBaseEntity().CreateTime
		}
	}

	if maxCreateTime <= createTime {
		return
	}

	value := make(map[string]interface{})
	value["createTime"] = maxCreateTime
	commRedisExchange.SaveConfigValue(commEnv.Service.ServiceType, commEnv.Service.ServiceName, "uploadTelemetryRTCursor", value)
}

// gatewayEUI 按网关进行记录的分组
func (e *telemetryRTPublish) groupByGatewayEUI(entityList []*edgeEntity.DeviceSequenceEntity) map[string][]*edgeEntity.DeviceSequenceEntity {
	result := make(map[string][]*edgeEntity.DeviceSequenceEntity)

	for _, recordEntity := range entityList {
		// 找到deviceEntity
		deviceEntity := edgeEntity.Type.DeviceEntity.Instance()
		deviceEntity.DeviceName = recordEntity.DeviceName
		entity, err := commEntityManager.GetEntityByServiceKey(deviceEntity.Type(), deviceEntity.MakeServiceKey())
		if err != nil || entity == nil {
			continue
		}
		deviceEntity = entity.(*edgeEntity.DeviceEntity)

		// 取出配置的云端侧设备编号
		thingspanelDeviceNumber := Map.GetString(deviceEntity.ExtendParam, "thingspanelDeviceNumber", "")
		if thingspanelDeviceNumber == "" {
			continue
		}

		// 将记录的设备名称，修改为thingspanel的设备编号
		recordEntity.DeviceName = thingspanelDeviceNumber

		// 找到deviceEntity绑定的channelEntity
		channelEntity := edgeEntity.Type.ChannelEntity.Instance()
		channelEntity.ChannelName = deviceEntity.ChannelName
		channelEntity.ChannelType = deviceEntity.ChannelType
		entity, err = commEntityManager.GetEntityByServiceKey(channelEntity.Type(), channelEntity.MakeServiceKey())
		if err != nil || entity == nil {
			continue
		}
		channelEntity = entity.(*edgeEntity.ChannelEntity)

		// 从channelEntity中取出gatewayEUI
		gatewayEUI := Map.GetString(channelEntity.ChannelParam, "gatewayEUI", "")

		list := result[gatewayEUI]
		if list == nil {
			list = make([]*edgeEntity.DeviceSequenceEntity, 0)
		}
		list = append(list, recordEntity)

		result[gatewayEUI] = list
	}

	return result
}

func (e *telemetryRTPublish) buildVO(gatewayID, gatewayEUI string, group map[string][]*edgeEntity.DeviceSequenceEntity) map[string]interface{} {
	if gatewayEUI == "" {
		return e.buildL2VO(group)
	} else {
		return e.buildL3VO(gatewayID, gatewayEUI, group)
	}
}

func (e *telemetryRTPublish) buildFoxEdgeVO() map[string]interface{} {
	// Fox-Edge级别的数据
	foxEdgeData := make(map[string]interface{})
	foxEdgeData["datetime"] = time.Now().UnixMilli()
	foxEdgeData["cpuUID"] = edgeEnv.GetCPUID()
	foxEdgeData["envType"] = edgeEnv.GetEnvType()
	foxEdgeData["arch"] = edgeEnv.GetARCH()

	return foxEdgeData
}

func (e *telemetryRTPublish) buildSubDeviceDataVO(group map[string][]*edgeEntity.DeviceSequenceEntity) map[string]interface{} {
	// 先按【设备名称】>【时间】>【指标名称】>【数值数组】的层级关系，组织数据
	levels := make(map[string]interface{})
	for deviceName, objectList := range group {
		for _, record := range objectList {
			if len(record.MetricName) == 0 {
				continue
			}

			data := record.DeviceValue["data"]
			Map.SetValue(levels, deviceName, String.ToString(record.Timestamp), record.MetricName, data)
		}
	}

	// 按ThingsPanel的格式要求，组织数据
	subDeviceData := make(map[string]interface{})
	for deviceName, valDeviceName := range levels {
		list := make([]interface{}, 0)

		for time, values := range valDeviceName.(map[string]interface{}) {
			data := make(map[string]interface{})

			data["ts"] = Number.ParseInt64(time)
			data["datatype"] = "rt"
			data["values"] = values

			list = append(list, data)
		}

		subDeviceData[deviceName] = list
	}

	return subDeviceData
}

func (e *telemetryRTPublish) buildL2VO(group map[string][]*edgeEntity.DeviceSequenceEntity) map[string]interface{} {
	// L1：Fox-Edge级别的数据
	foxEdgeData := e.buildFoxEdgeVO()

	// L2：构造子设备的结构信息
	subDeviceData := e.buildSubDeviceDataVO(group)

	edgeVo := make(map[string]interface{})
	edgeVo["gateway_data"] = foxEdgeData
	edgeVo["sub_device_data"] = subDeviceData
	return edgeVo
}

func (e *telemetryRTPublish) buildL3VO(gatewayID, gatewayEUI string, group map[string][]*edgeEntity.DeviceSequenceEntity) map[string]interface{} {
	// L1：Fox-Edge级别的数据
	foxEdgeData := e.buildFoxEdgeVO()

	// L2：构造网关的信息
	gatewayData := make(map[string]interface{})
	gatewayData["gatewayEUI"] = gatewayEUI

	// L3：构造子设备的结构信息
	subDeviceData := e.buildSubDeviceDataVO(group)

	gatewayVo := make(map[string]interface{})
	gatewayVo["gateway_data"] = gatewayData
	gatewayVo["sub_device_data"] = subDeviceData

	gatewaysVo := make(map[string]interface{})
	gatewaysVo[gatewayID] = gatewayVo

	edgeVo := make(map[string]interface{})
	edgeVo["gateway_data"] = foxEdgeData
	edgeVo["sub_gateway_data"] = gatewaysVo
	return edgeVo
}

func (e *telemetryRTPublish) buildValueVo(deviceValue map[string]interface{}) map[string]interface{} {
	ts := int64(0)
	values := make(map[string]interface{})
	for k, v := range deviceValue {
		values[k] = v.(map[string]interface{})["value"]
		ts = Map.GetInt64(v.(map[string]interface{}), "time", 0)
	}

	data := make(map[string]interface{})
	data["ts"] = ts
	data["values"] = values

	return data
}
